package com.wei.czz.framework.admin.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.handler.WriteHandler;
import com.wei.czz.common.annotation.Auth;
import com.wei.czz.common.annotation.Resubmit;
import com.wei.czz.common.dto.admin.dept.DeptDialogEnumDto;
import com.wei.czz.common.dto.admin.dept.DeptPageEnumDto;
import com.wei.czz.common.dto.admin.CheckDeptDto;
import com.wei.czz.common.dto.admin.DeptDto;
import com.wei.czz.common.dto.common.NameValue;
import com.wei.czz.common.dto.common.UploadDto;
import com.wei.czz.common.easyexcel.handler.CzzWriteHandler;
import com.wei.czz.common.easyexcel.listener.ObjectUploadListener;
import com.wei.czz.common.enums.CommonEnum;
import com.wei.czz.common.enums.admin.dept.BusinessTypeEnum;
import com.wei.czz.common.jsr303.group.Save;
import com.wei.czz.common.jsr303.group.Search;
import com.wei.czz.common.jsr303.group.Update;
import com.wei.czz.common.property.BatchProperty;
import com.wei.czz.common.tool.PageDto;
import com.wei.czz.common.tool.Result;
import com.wei.czz.common.utils.CopyUtils;
import com.wei.czz.common.utils.ExcelUtils;
import com.wei.czz.common.vo.admin.dept.DeptUploadVo;
import com.wei.czz.common.vo.common.UpdateStatusVo;
import com.wei.czz.common.vo.admin.dept.DeptFormVo;
import com.wei.czz.common.vo.admin.dept.DeptVo;
import com.wei.czz.framework.admin.manager.DeptManager;
import lombok.AllArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * Created by IntelliJ IDEA.
 *
 * @author wei
 * date: 2022-08-16 21:18:56
 * className: DeptController 系统部门操作请求接口类
 * version: 1.0
 * description:
 */
@RestController
@RequestMapping("/sys/dept")
@AllArgsConstructor
public class DeptController {

    private static final Logger log = LoggerFactory.getLogger(DeptController.class);

    private final BatchProperty batchProperty;

    private final DeptManager deptManager;

    /**
     * 保存部门数据接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页面-部门操作弹出框中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param deptVo 部门参数对象
     * @return 不返回数据
     */
    @Resubmit(message = "保存部门")
    @PostMapping("/save")
    @Auth(permit = "system:dept:save")
    public Result<Object> saveDept(@Validated(Save.class) @RequestBody DeptVo deptVo){
        log.info("开始保存部门数据业务。{}", deptVo);

        deptManager.saveDept(deptVo);

        log.info("保存部门数据业务结束");
        return Result.success();
    }

    /**
     * 上传部门接口
     * <p>
     * 调用方：
     *  1、浏览器-后台-部门管理页面-上传抽屉中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param file 文件对象
     * @return 结果
     */
    @Resubmit(spEL = "'uploadDept' + #userId", message = "上传部门")
    @PostMapping("/upload")
    @Auth(permit = "system:dept:save")
    public Result<UploadDto<DeptUploadVo>> uploadDept(@RequestParam MultipartFile file,
                                                      @RequestParam("mustAll") Boolean mustAll) {
        log.info("开始上传部门业务。mustAll={} fileName={} fileSize={}", mustAll, file.getOriginalFilename(), file.getSize());

        ObjectUploadListener<DeptUploadVo> objectUploadListener = new ObjectUploadListener<>();
        try {
            // 解析数据
            EasyExcel.read(file.getInputStream(), DeptUploadVo.class, objectUploadListener).sheet().doRead();

        } catch (IOException e) {
            log.error("部门上传文件解析异常。message={}", e.getMessage(), e);
            return Result.fail();
        }

        if (objectUploadListener.isEmpty()) {
            log.info("部门导入文件内容为空");
            return Result.clientFail("部门导入文件内容不能为空");
        }
        if (batchProperty.getMaxSize() < objectUploadListener.getTotal()) {
            log.warn("部门导入数量超限。batch.maxSize={} list.size={}", batchProperty.getMaxSize(),
                    objectUploadListener.getTotal());
            return Result.clientFail("一次导入部门操作最多允许同时导入" + batchProperty.getMaxSize() + "个部门");
        }

        UploadDto<DeptUploadVo> uploadDto = deptManager.uploadDept(mustAll, objectUploadListener);

        log.info("上传部门业务结束");
        return Result.success(uploadDto);
    }

    /**
     * 分页获取部门接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页面调用
     *
     * @param deptFormVo 部门查询参数对象
     * @return 部门数据列表
     */
    @Resubmit(message = "分页获取部门")
    @PostMapping("/page/list")
    @Auth(permit = "system:dept:list")
    public Result<PageDto<DeptDto>> getDeptList(@Validated(Search.class) @RequestBody DeptFormVo deptFormVo){
        log.info("开始查询部门数据列表业务。{}", deptFormVo);

        PageDto<DeptDto> pageDto = deptManager.getPageDeptList(deptFormVo);

        log.info("查询部门数据列表业务结束");
        return Result.success(pageDto);
    }

    /**
     * 获取部门管理页面参数接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页面调用
     * @return 结果
     */
    @GetMapping("/page/param")
    @Auth(permit = "system:dept:list")
    public Result<DeptPageEnumDto> getDeptPageParam() {
        log.info("开始获取部门管理页面参数业务");

        DeptPageEnumDto deptPageEnumDto = deptManager.getDeptPageParam();

        log.info("获取部门管理页面参数业务结束");
        return Result.success(deptPageEnumDto);
    }

    /**
     * 部门操作弹出框枚举接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页-部门操作弹出框中调用
     * @param parentId 父级部门主键，用于查询下一个排序等级
     * @param deptId   部门主键
     * @return 结果
     */
    @GetMapping("/dialog/enum")
    @Auth(permit = { "system:dept:save", "system:dept:update"})
    public Result<DeptDialogEnumDto> getDialogEnum(@RequestParam(value = "parentId", required = false) Long parentId,
                                                   @RequestParam(value = "deptId", required = false) Long deptId) {
        log.info("开始部门操作弹出框枚举业务。parentId={} deptId={}", parentId, deptId);

        DeptDialogEnumDto enumDto = deptManager.getDialogEnum(parentId, deptId);

        log.info("部门操作弹出框枚举业务结束");
        return Result.success(enumDto);
    }

    /**
     * 查询部门树接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-角色授权组管理页-用户选择弹出框中调用
     *
     * @param deptId 部门主键
     * @return 部门树数据
     */
    @GetMapping("/tree")
    @Auth(permit = { "system:dept:save", "system:dept:update", "system:role-space:save", "system:role-space:update" })
    public Result<DeptDto> getDeptTree(@RequestParam(value = "deptId", required = false) Long deptId) {
        log.info("开始查询部门树数据业务");

        DeptDto deptDto = deptManager.getDeptTree(deptId);

        log.info("查询部门树数据业务结束");
        return Result.success(deptDto);
    }

    /**
     * 获取子部门树接口
     * 根据部门主键参数，查询子部门数据，并构造成部门树结构
     * <p>
     * 调用方：
     *  2、浏览器-后台管理页-用户管理页中调用
     *  3、浏览器-后台管理页-用户管理页-用户切换部门弹出框中调用
     *  4、浏览器-后台管理页-用户管理页-批量用户切换部门弹出框中调用
     *
     * @param id 父级部门主键
     * @return 子部门树数据
     */
    @GetMapping("/children/tree")
    @Auth(permit = { "system:role:list", "user:move:opt" })
    public Result<List<DeptDto>> getChildrenDeptTree(@RequestParam("id") Long id) {
        log.info("开始获取子部门树数据业务。deptId={}", id);

        List<DeptDto> deptDtoList = deptManager.getChildrenDeptTree(id);

        log.info("获取子部门树数据业务结束");
        return Result.success(deptDtoList);
    }

    /**
     * 删除部门数据前置校验接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页中调用
     *
     * @param id 部门主键
     * @return 校验结果
     */
    @GetMapping("/delete/check")
    @Auth(permit = "system:dept:get")
    public Result<CheckDeptDto> deleteDeptCheck(@RequestParam("id") Long id) {
        log.info("开始删除部门数据前置校验业务。id={}", id);

        CheckDeptDto checkDeptDto = deptManager.deleteDeptCheck(id);

        log.info("删除部门数据前置校验业务结束");
        return Result.success(checkDeptDto);
    }

    /**
     * 获取下一个默认排序等级接口
     * 根据部门主键，获取该部门下子部门的下一个默认排序等级
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页-部门操作弹出框中调用
     *
     * @param id 父级部门主键
     * @return 下一个默认排序等级数据
     */
    @GetMapping("/next/sort")
    @Auth(permit = "system:dept:get")
    public Result<Integer> getNextSort(@RequestParam(value = "id", required = false) Long id) {
        log.info("开始获取下一个默认排序等级业务。id={}", id);

        Integer nextSort = deptManager.getNextSort(id);

        log.info("获取下一个默认排序等级业务结束");
        return Result.success(nextSort);
    }

    /**
     * 下载部门导入模板接口
     * <p>
     * 调用方：
     *  1、浏览器-后台-部门管理页面-上传抽屉中调用
     * @param response 响应对象
     * @return 结果
     */
    @GetMapping("/download/upload-template")
    @Auth(permit = "system:dept:save")
    public Result<Object> downloadUploadTemplate(HttpServletResponse response) {
        log.info("开始下载部门导入模板业务");

        List<DeptUploadVo> list = new ArrayList<>(4);

        DeptUploadVo deptUploadVo = new DeptUploadVo();
        deptUploadVo.setSerial(1);
        deptUploadVo.setParentNo(CommonEnum.ZERO.getLongValue());
        deptUploadVo.setName("第一层级部门，父级部门主键为0");
        deptUploadVo.setBusinessTypeName(BusinessTypeEnum.ORG.getName());
        deptUploadVo.setSort(1);
        list.add(deptUploadVo);

        deptUploadVo = new DeptUploadVo();
        deptUploadVo.setSerial(2);
        deptUploadVo.setParentNo(324905529254150144L);
        deptUploadVo.setName("测试部门");
        deptUploadVo.setBusinessTypeName(BusinessTypeEnum.ORG.getName());
        deptUploadVo.setSort(1);
        list.add(deptUploadVo);

        deptUploadVo = new DeptUploadVo();
        deptUploadVo.setSerial(3);
        deptUploadVo.setParentNo(2L);
        deptUploadVo.setName("测试部门的子部门1");
        deptUploadVo.setBusinessTypeName(BusinessTypeEnum.ORG.getName());
        deptUploadVo.setSort(1);
        list.add(deptUploadVo);

        deptUploadVo = new DeptUploadVo();
        deptUploadVo.setSerial(4);
        deptUploadVo.setParentNo(2L);
        deptUploadVo.setName("测试部门的子部门2");
        deptUploadVo.setBusinessTypeName(BusinessTypeEnum.ORG.getName());
        deptUploadVo.setSort(2);
        list.add(deptUploadVo);

        // 设置数据列的枚举列表映射
        Map<Integer, List<String>> enumMap = Collections.singletonMap(3, CopyUtils.mapList(BusinessTypeEnum.getList(), NameValue::getName));

        CzzWriteHandler czzWriteHandler = new CzzWriteHandler(500, enumMap);

        List<WriteHandler> writeHandlerList = ExcelUtils.basicWriteHandlerList();
        writeHandlerList.add(czzWriteHandler);

        ExcelUtils.writeResponse("部门导入模板", list, deptUploadVo.getTemplatePropertyList(),
                DeptUploadVo.class, writeHandlerList, response);

        log.info("下载部门导入模板业务结束");
        return null;
    }

    /**
     * 部门自选数据导出接口
     * @param taskId 任务主键
     * @param response http响应杜祥
     * @return 结果
     */
    @GetMapping("/choice-export")
    @Auth(permit = "system:dept:save")
    public Result<Object> deptChoiceExport(@RequestParam("taskId") Long taskId, HttpServletResponse response) {
        log.info("开始部门自选数据导出业务。taskId={}", taskId);

        deptManager.deptChoiceExport(taskId, response);

        log.info("部门自选数据导出业务结束");
        return null;
    }

    /**
     * 修改部门数据接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页面-部门操作弹出框中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param deptVo 部门参数对象
     * @return 不返回数据
     */
    @Resubmit(message = "修改部门")
    @PostMapping("/update")
    @Auth(permit = "system:dept:update")
    public Result<Object> updateDept(@Validated(Update.class) @RequestBody DeptVo deptVo){
        log.info("开始修改部门数据业务。{}", deptVo);

        deptManager.updateDept(deptVo);

        log.info("修改部门数据业务结束");
        return Result.success();
    }

    /**
     * 修改部门状态接口
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param updateStatusVo 参数对象
     * @return 不返回数据
     */
    @Resubmit(message = "修改部门状态")
    @PostMapping("/update/status")
    @Auth(permit = "system:dept:update")
    public Result<Object> updateDeptStatus(@Validated @RequestBody UpdateStatusVo updateStatusVo) {
        log.info("开始修改部门状态业务。{}", updateStatusVo);

        deptManager.updateDeptStatus(updateStatusVo.getIdList().get(0), updateStatusVo.getStatus());

        log.info("修改部门状态业务结束");
        return Result.success();
    }

    /**
     * 删除部门及其子部门数据接口
     * 根据部门主键，删除该部门及其子部门的数据
     * <p>
     * 调用方：
     *  1、浏览器-后台管理页面-部门管理页面-部门删除提示弹出框中调用
     * <p>
     * 此接口会校验是否重复提交
     * @param id 部门主键
     * @return 不返回数据
     */
    @Resubmit(message = "删除部门")
    @DeleteMapping("/delete")
    @Auth(permit = "system:dept:delete")
    public Result<Object> deleteDept(@RequestParam("id") Long id) {
        log.info("开始删除部门及其子部门数据业务。id={}", id);

        deptManager.deleteDept(id);

        log.info("删除部门及其子部门数据业务结束");
        return Result.success();
    }

}
